using System;
using System.Diagnostics;
using Community.CsharpSqlite.Entity;

namespace Community.CsharpSqlite
{
    using DbPage = PgHdr;

    public  class Backup
    {
        /*
    ** 2009 January 28
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** This file contains the implementation of the sqlite3_backup_XXX()
    ** API functions and the related features.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-03-09 19:31:43 4ae453ea7be69018d8c16eb8dabe05617397dc4d
    **
    **  $Header: Community.CsharpSqlite/src/backup_c.cs,v 6604176a7dbe 2010/03/12 23:35:36 Noah $
    *************************************************************************
    */
        //#include "sqliteInt.h"
        //#include "btreeInt.h"

        /* Macro to find the minimum of two numeric values.
    */
#if !MIN
        //# define MIN(x,y) ((x)<(y)?(x):(y))
#endif


        /*
    ** THREAD SAFETY NOTES:
    **
    **   Once it has been created using backup_init(), a single sqlite3_backup
    **   structure may be accessed via two groups of thread-safe entry points:
    **
    **     * Via the sqlite3_backup_XXX() API function backup_step() and
    **       backup_finish(). Both these functions obtain the source database
    **       handle mutex and the mutex associated with the source BtShared
    **       structure, in that order.
    **
    **     * Via the BackupUpdate() and BackupRestart() functions, which are
    **       invoked by the pager layer to report various state changes in
    **       the page cache associated with the source database. The mutex
    **       associated with the source database BtShared structure will always
    **       be held when either of these functions are invoked.
    **
    **   The other sqlite3_backup_XXX() API functions, backup_remaining() and
    **   backup_pagecount() are not thread-safe functions. If they are called
    **   while some other thread is calling backup_step() or backup_finish(),
    **   the values returned may be invalid. There is no way for a call to
    **   BackupUpdate() or BackupRestart() to interfere with backup_remaining()
    **   or backup_pagecount().
    **
    **   Depending on the SQLite configuration, the database handles and/or
    **   the Btree objects may have their own mutexes that require locking.
    **   Non-sharable Btrees (in-memory databases for example), do not have
    **   associated mutexes.
    */

        /*
    ** Return a pointer corresponding to database zDb (i.e. "main", "temp")
    ** in connection handle pDb. If such a database cannot be found, return
    ** a NULL pointer and write an error message to pErrorDb.
    **
    ** If the "temp" database is requested, it may need to be opened by this
    ** function. If an error occurs while doing so, return 0 and write an
    ** error message to pErrorDb.
    */

        private static Btree findBtree(sqlite3 pErrorDb, sqlite3 pDb, string zDb)
        {
            int i = Build.FindDbName(pDb, zDb);

            if (i == 1)
            {
                Parse pParse;
                int rc = 0;
                pParse = new Parse(); //sqlite3StackAllocZero(pErrorDb, sizeof(*pParse));
                if (pParse == null)
                {
                    Utility.Sqlite3Error(pErrorDb, StatusCode.SQLITE_NOMEM, "out of memory");
                    rc = StatusCode.SQLITE_NOMEM;
                }
                else
                {
                    pParse.db = pDb;
                    if (Build.OpenTempDatabase(pParse) != 0)
                    {
                        Utility.Sqlite3Error(pErrorDb, pParse.rc, "%s", pParse.zErrMsg);
                        rc = StatusCode.SQLITE_ERROR;
                    }
                    MemPool.DbFree(pErrorDb, ref pParse.zErrMsg);
                    //sqlite3StackFree( pErrorDb, pParse );
                }
                if (rc != 0)
                {
                    return null;
                }
            }

            if (i < 0)
            {
                Utility.Sqlite3Error(pErrorDb, StatusCode.SQLITE_ERROR, "unknown database %s", zDb);
                return null;
            }

            return pDb.aDb[i].pBt;
        }

        /*
    ** Create an sqlite3_backup process to copy the contents of zSrcDb from
    ** connection handle pSrcDb to zDestDb in pDestDb. If successful, return
    ** a pointer to the new sqlite3_backup object.
    **
    ** If an error occurs, NULL is returned and an error code and error message
    ** stored in database handle pDestDb.
    */

        public static sqlite3_backup sqlite3_backup_init(
            sqlite3 pDestDb, /* Database to write to */
            string zDestDb, /* Name of database within pDestDb */
            sqlite3 pSrcDb, /* Database connection to read from */
            string zSrcDb /* Name of database within pSrcDb */
            )
        {
            sqlite3_backup p; /* Value to return */

            /* Lock the source database handle. The destination database
      ** handle is not locked in this routine, but it is locked in
      ** sqlite3_backup_step(). The user is required to ensure that no
      ** other thread accesses the destination handle for the duration
      ** of the backup operation.  Any attempt to use the destination
      ** database connection while a backup is in progress may cause
      ** a malfunction or a deadlock.
      */
            MutexHelper.MutexEnter(pSrcDb.mutex);
            MutexHelper.MutexEnter(pDestDb.mutex);

            if (pSrcDb == pDestDb)
            {
                Utility.Sqlite3Error(
                    pDestDb, StatusCode.SQLITE_ERROR, "source and destination must be distinct"
                    );
                p = null;
            }
            else
            {
                /* Allocate space for a new sqlite3_backup object */
                p = new sqlite3_backup(); // (sqlite3_backup)Malloc.sqlite3_malloc( sizeof( sqlite3_backup ) );
                //if ( null == p )
                //{
                //  Utility.Sqlite3Error( pDestDb, StatusCode.SQLITE_NOMEM, 0 );
                //}
            }

            /* If the allocation succeeded, populate the new object. */
            if (p != null)
            {
                // memset( p, 0, sizeof( sqlite3_backup ) );
                p.pSrc = findBtree(pDestDb, pSrcDb, zSrcDb);
                p.pDest = findBtree(pDestDb, pDestDb, zDestDb);
                p.pDestDb = pDestDb;
                p.pSrcDb = pSrcDb;
                p.iNext = 1;
                p.isAttached = 0;

                if (null == p.pSrc || null == p.pDest)
                {
                    /* One (or both) of the named databases did not exist. An error has
          ** already been written into the pDestDb handle. All that is left
          ** to do here is free the sqlite3_backup structure.
          */
                    //Malloc.sqlite3_free( ref p );
                    p = null;
                }
            }

            if (p != null)
            {
                p.pSrc.nBackup++;
            }

            MutexHelper.MutexLeave(pDestDb.mutex);
            MutexHelper.MutexLeave(pSrcDb.mutex);
            return p;
        }

        /*
    ** Argument rc is an SQLite error code. Return true if this error is
    ** considered fatal if encountered during a backup operation. All errors
    ** are considered fatal except for StatusCode.SQLITE_BUSY and StatusCode.SQLITE_LOCKED.
    */

        private static bool isFatalError(int rc)
        {
            return (rc != StatusCode.SQLITE_OK && rc != StatusCode.SQLITE_BUSY && UnitTest.ALWAYS(rc != StatusCode.SQLITE_LOCKED));
        }

        /*
    ** Parameter zSrcData points to a buffer containing the data for
    ** page iSrcPg from the source database. Copy this data into the
    ** destination database.
    */

        private static int backupOnePage(sqlite3_backup p, uint iSrcPg, byte[] zSrcData)
        {
            Pager pDestPager = sqlite3BtreePager(p.pDest);
            int nSrcPgsz = sqlite3BtreeGetPageSize(p.pSrc);
            int nDestPgsz = sqlite3BtreeGetPageSize(p.pDest);
            int nCopy = Math.Min(nSrcPgsz, nDestPgsz);
            long iEnd = iSrcPg*nSrcPgsz;

            int rc = StatusCode.SQLITE_OK;
            long iOff;

            Debug.Assert(p.bDestLocked != 0);
            Debug.Assert(!isFatalError(p.rc));
            Debug.Assert(iSrcPg != Utility.PENDING_BYTE_PAGE(p.pSrc.pBt));
            Debug.Assert(zSrcData != null);

            /* Catch the case where the destination is an in-memory database and the
      ** page sizes of the source and destination differ.
      */
            if (nSrcPgsz != nDestPgsz && sqlite3PagerIsMemdb(sqlite3BtreePager(p.pDest)))
            {
                rc = StatusCode.SQLITE_READONLY;
            }

            /* This loop runs once for each destination page spanned by the source
      ** page. For each iteration, variable iOff is set to the byte offset
      ** of the destination page.
      */
            for (iOff = iEnd - (long) nSrcPgsz; rc == StatusCode.SQLITE_OK && iOff < iEnd; iOff += nDestPgsz)
            {
                DbPage pDestPg = null;
                uint iDest = (uint) (iOff/nDestPgsz) + 1;
                if (iDest == Utility.PENDING_BYTE_PAGE(p.pDest.pBt)) continue;
                if (StatusCode.SQLITE_OK == (rc = sqlite3PagerGet(pDestPager, iDest, ref pDestPg))
                    && StatusCode.SQLITE_OK == (rc = PagerHelper.PagerWrite(pDestPg))
                    )
                {
                    //string zIn = &zSrcData[iOff%nSrcPgsz];
                    byte[] zDestData = sqlite3PagerGetData(pDestPg);
                    //string zOut = &zDestData[iOff % nDestPgsz];

                    /* Copy the data from the source page into the destination page.
          ** Then clear the Btree layer MemPage.isInit flag. Both this module
          ** and the pager code use this trick (clearing the first byte
          ** of the page 'extra' space to invalidate the Btree layers
          ** cached parse of the page). MemPage.isInit is marked
          ** "MUST BE FIRST" for this purpose.
          */
                    Buffer.BlockCopy(zSrcData, (int) (iOff%nSrcPgsz), zDestData, (int) (iOff%nDestPgsz), nCopy);
                        // memcpy( zOut, zIn, nCopy );
                    sqlite3PagerGetExtra(pDestPg).isInit = 0; // ( sqlite3PagerGetExtra( pDestPg ) )[0] = 0;
                }
                sqlite3PagerUnref(pDestPg);
            }

            return rc;
        }

        /*
    ** If pFile is currently larger than iSize bytes, then truncate it to
    ** exactly iSize bytes. If pFile is not larger than iSize bytes, then
    ** this function is a no-op.
    **
    ** Return StatusCode.SQLITE_OK if everything is successful, or an SQLite error
    ** code if an error occurs.
    */

        private static int backupTruncateFile(sqlite3_file pFile, int iSize)
        {
            int iCurrent = 0;
            int rc = sqlite3OsFileSize(pFile, ref iCurrent);
            if (rc == StatusCode.SQLITE_OK && iCurrent > iSize)
            {
                rc = sqlite3OsTruncate(pFile, iSize);
            }
            return rc;
        }

        /*
    ** Register this backup object with the associated source pager for
    ** callbacks when pages are changed or the cache invalidated.
    */

        private static void attachBackupObject(sqlite3_backup p)
        {
            sqlite3_backup pp;
            Debug.Assert(sqlite3BtreeHoldsMutex(p.pSrc));
            pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p.pSrc));
            p.pNext = pp;
            sqlite3BtreePager(p.pSrc).pBackup = p; //*pp = p;
            p.isAttached = 1;
        }

        /*
    ** Copy nPage pages from the source b-tree to the destination.
    */

        public static int sqlite3_backup_step(sqlite3_backup p, int nPage)
        {
            int rc;

            MutexHelper.MutexEnter(p.pSrcDb.mutex);
            sqlite3BtreeEnter(p.pSrc);
            if (p.pDestDb != null)
            {
                MutexHelper.MutexEnter(p.pDestDb.mutex);
            }

            rc = p.rc;
            if (!isFatalError(rc))
            {
                Pager pSrcPager = sqlite3BtreePager(p.pSrc); /* Source pager */
                Pager pDestPager = sqlite3BtreePager(p.pDest); /* Dest pager */
                int ii; /* Iterator variable */
                int nSrcPage = -1; /* Size of source db in pages */
                int bCloseTrans = 0; /* True if src db requires unlocking */

                /* If the source pager is currently in a write-transaction, return
        ** StatusCode.SQLITE_BUSY immediately.
        */
                if (p.pDestDb != null && p.pSrc.pBt.inTransaction == TransactionType.TRANS_WRITE)
                {
                    rc = StatusCode.SQLITE_BUSY;
                }
                else
                {
                    rc = StatusCode.SQLITE_OK;
                }

                /* Lock the destination database, if it is not locked already. */
                if (StatusCode.SQLITE_OK == rc && p.bDestLocked == 0
                    && StatusCode.SQLITE_OK == (rc = sqlite3BtreeBeginTrans(p.pDest, 2))
                    )
                {
                    p.bDestLocked = 1;
                    sqlite3BtreeGetMeta(p.pDest, BtreeMeta.BTREE_SCHEMA_VERSION, ref p.iDestSchema);
                }

                /* If there is no open read-transaction on the source database, open
        ** one now. If a transaction is opened here, then it will be closed
        ** before this function exits.
        */
                if (rc == StatusCode.SQLITE_OK && !sqlite3BtreeIsInReadTrans(p.pSrc))
                {
                    rc = sqlite3BtreeBeginTrans(p.pSrc, 0);
                    bCloseTrans = 1;
                }

                /* Now that there is a read-lock on the source database, query the
        ** source pager for the number of pages in the database.
        */
                if (rc == StatusCode.SQLITE_OK)
                {
                    rc = sqlite3PagerPagecount(pSrcPager, ref nSrcPage);
                }
                for (ii = 0; (nPage < 0 || ii < nPage) && p.iNext <= (uint) nSrcPage && 0 == rc; ii++)
                {
                    uint iSrcPg = p.iNext; /* Source page number */
                    if (iSrcPg != Utility.PENDING_BYTE_PAGE(p.pSrc.pBt))
                    {
                        DbPage pSrcPg = null; /* Source page object */
                        rc = sqlite3PagerGet(pSrcPager, iSrcPg, ref pSrcPg);
                        if (rc == StatusCode.SQLITE_OK)
                        {
                            rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg));
                            sqlite3PagerUnref(pSrcPg);
                        }
                    }
                    p.iNext++;
                }
                if (rc == StatusCode.SQLITE_OK)
                {
                    p.nPagecount = (uint) nSrcPage;
                    p.nRemaining = (uint) (nSrcPage + 1 - p.iNext);
                    if (p.iNext > (uint) nSrcPage)
                    {
                        rc = StatusCode.SQLITE_DONE;
                    }
                    else if (0 == p.isAttached)
                    {
                        attachBackupObject(p);
                    }
                }


                /* Update the schema version field in the destination database. This
        ** is to make sure that the schema-version really does change in
        ** the case where the source and destination databases have the
        ** same schema version.
        */
                if (rc == StatusCode.SQLITE_DONE
                    && (rc = sqlite3BtreeUpdateMeta(p.pDest, 1, p.iDestSchema + 1)) == StatusCode.SQLITE_OK
                    )
                {
                    int nSrcPagesize = sqlite3BtreeGetPageSize(p.pSrc);
                    int nDestPagesize = sqlite3BtreeGetPageSize(p.pDest);
                    int nDestTruncate;
                    if (p.pDestDb != null)
                    {
                        Build.ResetInternalSchema(p.pDestDb, 0);
                    }

                    /* Set nDestTruncate to the final number of pages in the destination
          ** database. The complication here is that the destination page
          ** size may be different to the source page size.
          **
          ** If the source page size is smaller than the destination page size,
          ** round up. In this case the call to sqlite3OsTruncate() below will
          ** fix the size of the file. However it is important to call
          ** sqlite3PagerTruncateImage() here so that any pages in the
          ** destination file that lie beyond the nDestTruncate page mark are
          ** journalled by PagerCommitPhaseOne() before they are destroyed
          ** by the file truncation.
          */
                    if (nSrcPagesize < nDestPagesize)
                    {
                        int ratio = nDestPagesize/nSrcPagesize;
                        nDestTruncate = (nSrcPage + ratio - 1)/ratio;
                        if (nDestTruncate == (int) Utility.PENDING_BYTE_PAGE(p.pDest.pBt))
                        {
                            nDestTruncate--;
                        }
                    }
                    else
                    {
                        nDestTruncate = nSrcPage*(nSrcPagesize/nDestPagesize);
                    }
                    sqlite3PagerTruncateImage(pDestPager, (uint) nDestTruncate);

                    if (nSrcPagesize < nDestPagesize)
                    {
                        /* If the source page-size is smaller than the destination page-size,
            ** two extra things may need to happen:
            **
            **   * The destination may need to be truncated, and
            **
            **   * Data stored on the pages immediately following the
            **     pending-byte page in the source database may need to be
            **     copied into the destination database.
            */
                        uint iSize = (uint) nSrcPagesize*(uint) nSrcPage;
                        sqlite3_file pFile = sqlite3PagerFile(pDestPager);

                        Debug.Assert(pFile != null);
                        Debug.Assert(nDestTruncate*(long) nDestPagesize >= iSize || (
                                                                                        nDestTruncate ==
                                                                                        (int)
                                                                                        (Utility.PENDING_BYTE_PAGE(p.pDest.pBt) -
                                                                                         1)
                                                                                        && iSize >= Global.PENDING_BYTE &&
                                                                                        iSize <=
                                                                                        Global.PENDING_BYTE + nDestPagesize
                                                                                    ));
                        if (StatusCode.SQLITE_OK == (rc = sqlite3PagerCommitPhaseOne(pDestPager, null, true))
                            && StatusCode.SQLITE_OK == (rc = backupTruncateFile(pFile, (int) iSize))
                            && StatusCode.SQLITE_OK == (rc = sqlite3PagerSync(pDestPager))
                            )
                        {
                            long iOff;
                            long iEnd = Math.Min(Global.PENDING_BYTE + nDestPagesize, iSize);
                            for (
                                iOff = Global.PENDING_BYTE + nSrcPagesize;
                                rc == StatusCode.SQLITE_OK && iOff < iEnd;
                                iOff += nSrcPagesize
                                )
                            {
                                PgHdr pSrcPg = null;
                                var iSrcPg = (uint) ((iOff/nSrcPagesize) + 1);
                                rc = sqlite3PagerGet(pSrcPager, iSrcPg, ref pSrcPg);
                                if (rc == StatusCode.SQLITE_OK)
                                {
                                    byte[] zData = sqlite3PagerGetData(pSrcPg);
                                    rc = sqlite3OsWrite(pFile, zData, nSrcPagesize, iOff);
                                }
                                sqlite3PagerUnref(pSrcPg);
                            }
                        }
                    }
                    else
                    {
                        rc = sqlite3PagerCommitPhaseOne(pDestPager, null, false);
                    }

                    /* Finish committing the transaction to the destination database. */
                    if (StatusCode.SQLITE_OK == rc
                        && StatusCode.SQLITE_OK == (rc = sqlite3BtreeCommitPhaseTwo(p.pDest))
                        )
                    {
                        rc = StatusCode.SQLITE_DONE;
                    }
                }

                /* If bCloseTrans is true, then this function opened a read transaction
        ** on the source database. Close the read transaction here. There is
        ** no need to check the return values of the btree methods here, as
        ** "committing" a read-only transaction cannot fail.
        */
                if (bCloseTrans != 0)
                {
#if !NDEBUG || SQLITE_COVERAGE_TEST
    //TESTONLY( int rc2 );
    //TESTONLY( rc2  = ) sqlite3BtreeCommitPhaseOne(p.pSrc, 0);
    //TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p.pSrc);
          int rc2;
          rc2 = sqlite3BtreeCommitPhaseOne( p.pSrc, "" );
          rc2 |= sqlite3BtreeCommitPhaseTwo( p.pSrc );
          Debug.Assert( rc2 == StatusCode.SQLITE_OK );
#else
                    sqlite3BtreeCommitPhaseOne(p.pSrc, null);
                    sqlite3BtreeCommitPhaseTwo(p.pSrc);
#endif
                }

                p.rc = rc;
            }
            if (p.pDestDb != null)
            {
                MutexHelper.MutexLeave(p.pDestDb.mutex);
            }
            sqlite3BtreeLeave(p.pSrc);
            MutexHelper.MutexLeave(p.pSrcDb.mutex);
            return rc;
        }

        /*
    ** Release all resources associated with an sqlite3_backup* handle.
    */

        public static int sqlite3_backup_finish(sqlite3_backup p)
        {
            sqlite3_backup pp; /* Ptr to head of pagers backup list */
            sqlite3_mutex mutex; /* Mutex to protect source database */
            int rc; /* Value to return */

            /* Enter the mutexes */
            if (p == null) return StatusCode.SQLITE_OK;
            MutexHelper.MutexEnter(p.pSrcDb.mutex);
            sqlite3BtreeEnter(p.pSrc);
            mutex = p.pSrcDb.mutex;
            if (p.pDestDb != null)
            {
                MutexHelper.MutexEnter(p.pDestDb.mutex);
            }

            /* Detach this backup from the source pager. */
            if (p.pDestDb != null)
            {
                p.pSrc.nBackup--;
            }
            if (p.isAttached != 0)
            {
                pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p.pSrc));
                while (pp != p)
                {
                    pp = (pp).pNext;
                }
                sqlite3BtreePager(p.pSrc).pBackup = p.pNext;
            }

            /* If a transaction is still open on the Btree, roll it back. */
            sqlite3BtreeRollback(p.pDest);

            /* Set the error code of the destination database handle. */
            rc = (p.rc == StatusCode.SQLITE_DONE) ? StatusCode.SQLITE_OK : p.rc;
            Utility.Sqlite3Error(p.pDestDb, rc, 0);

            /* Exit the mutexes and free the backup context structure. */
            if (p.pDestDb != null)
            {
                MutexHelper.MutexLeave(p.pDestDb.mutex);
            }
            sqlite3BtreeLeave(p.pSrc);
            if (p.pDestDb != null)
            {
                //Malloc.sqlite3_free( ref p );
            }
            MutexHelper.MutexLeave(mutex);
            return rc;
        }

        /*
    ** Return the number of pages still to be backed up as of the most recent
    ** call to sqlite3_backup_step().
    */

        private static int sqlite3_backup_remaining(sqlite3_backup p)
        {
            return (int) p.nRemaining;
        }

        /*
    ** Return the total number of pages in the source database as of the most
    ** recent call to sqlite3_backup_step().
    */

        private static int sqlite3_backup_pagecount(sqlite3_backup p)
        {
            return (int) p.nPagecount;
        }

        /*
    ** This function is called after the contents of page iPage of the
    ** source database have been modified. If page iPage has already been
    ** copied into the destination database, then the data written to the
    ** destination is now invalidated. The destination copy of iPage needs
    ** to be updated with the new data before the backup operation is
    ** complete.
    **
    ** It is assumed that the mutex associated with the BtShared object
    ** corresponding to the source database is held when this function is
    ** called.
    */

        private static void sqlite3BackupUpdate(sqlite3_backup pBackup, uint iPage, byte[] aData)
        {
            sqlite3_backup p; /* Iterator variable */
            for (p = pBackup; p != null; p = p.pNext)
            {
                Debug.Assert(MutexHelper.MutexHeld(p.pSrc.pBt.mutex));
                if (!isFatalError(p.rc) && iPage < p.iNext)
                {
                    /* The backup process p has already copied page iPage. But now it
          ** has been modified by a transaction on the source pager. Copy
          ** the new data into the backup.
          */
                    int rc = backupOnePage(p, iPage, aData);
                    Debug.Assert(rc != StatusCode.SQLITE_BUSY && rc != StatusCode.SQLITE_LOCKED);
                    if (rc != StatusCode.SQLITE_OK)
                    {
                        p.rc = rc;
                    }
                }
            }
        }

        /*
    ** Restart the backup process. This is called when the pager layer
    ** detects that the database has been modified by an external database
    ** connection. In this case there is no way of knowing which of the
    ** pages that have been copied into the destination database are still
    ** valid and which are not, so the entire process needs to be restarted.
    **
    ** It is assumed that the mutex associated with the BtShared object
    ** corresponding to the source database is held when this function is
    ** called.
    */

        private static void sqlite3BackupRestart(sqlite3_backup pBackup)
        {
            sqlite3_backup p; /* Iterator variable */
            for (p = pBackup; p != null; p = p.pNext)
            {
                Debug.Assert(MutexHelper.MutexHeld(p.pSrc.pBt.mutex));
                p.iNext = 1;
            }
        }

#if !SQLITE_OMIT_VACUUM
        /*
** Copy the complete content of pBtFrom into pBtTo.  A transaction
** must be active for both files.
**
** The size of file pTo may be reduced by this operation. If anything
** goes wrong, the transaction on pTo is rolled back. If successful, the
** transaction is committed before returning.
*/

        private static int sqlite3BtreeCopyFile(Btree pTo, Btree pFrom)
        {
            int rc;
            sqlite3_backup b;
            sqlite3BtreeEnter(pTo);
            sqlite3BtreeEnter(pFrom);

            /* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set
      ** to 0. This is used by the implementations of sqlite3_backup_step()
      ** and sqlite3_backup_finish() to detect that they are being called
      ** from this function, not directly by the user.
      */
            b = new sqlite3_backup(); // memset( &b, 0, sizeof( b ) );
            b.pSrcDb = pFrom.db;
            b.pSrc = pFrom;
            b.pDest = pTo;
            b.iNext = 1;

            /* 0x7FFFFFFF is the hard limit for the number of pages in a database
      ** file. By passing this as the number of pages to copy to
      ** sqlite3_backup_step(), we can guarantee that the copy finishes
      ** within a single call (unless an error occurs). The Debug.Assert() statement
      ** checks this assumption - (p.rc) should be set to either StatusCode.SQLITE_DONE
      ** or an error code.
      */
            sqlite3_backup_step(b, 0x7FFFFFFF);
            Debug.Assert(b.rc != StatusCode.SQLITE_OK);
            rc = sqlite3_backup_finish(b);
            if (rc == StatusCode.SQLITE_OK)
            {
                pTo.pBt.pageSizeFixed = false;
            }

            sqlite3BtreeLeave(pFrom);
            sqlite3BtreeLeave(pTo);
            return rc;
        }
#endif
        //* SQLITE_OMIT_VACUUM */
    }
}